/*
 * 在Orchard Core中，如果您想处理资源文件，可以使用Media功能，它可以帮助您从仪表板管理租户的Media文件夹中的文件。
 * 如果启用了OrchardCore.Media.Azure，存储将是Azure Blob Storage。但如果您想直接在代码中上传、删除或读取文件，
 * 甚至在Media文件夹之外操作文件怎么办？
 *
 * 有一个名为IFileStore的服务，但它没有注册到服务提供者中。需要执行特定文件操作的功能有自己的服务，
 * 这些服务继承自IFileStore（或以某种方式依赖于它）。例如，如果您想管理Media文件夹中的文件，可以使用IMediaFileStore服务。
 * 只要启用了Media功能，就可以在代码中的任何地方访问它。
 *
 * 如果您想在文件系统的任何位置管理文件，需要有自己的服务，并使用FileSystemStore（实现了IFileStore接口），
 * 并指定自己的基础路径。这里也有相关示例。
 *
 * 主要功能：
 * - 演示Orchard Core文件管理机制
 * - 展示IMediaFileStore和自定义文件存储的用法
 * - 演示文件上传、读取、创建等操作
 * - 展示如何扩展文件存储服务
 */

using Lombiq.TrainingDemo.Services;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Localization;
using OrchardCore.DisplayManagement.Notify;
using OrchardCore.FileStorage;
using OrchardCore.Media;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace Lombiq.TrainingDemo.Controllers;

/// <summary>
/// 文件管理控制器 - 演示Orchard Core文件管理机制
/// 
/// 主要用途：
/// - 演示Orchard Core文件管理机制
/// - 展示IMediaFileStore和自定义文件存储的用法
/// - 演示文件上传、读取、创建等操作
/// - 展示如何扩展文件存储服务
/// 
/// 使用场景：
/// - 在Media文件夹中创建和读取文件
/// - 上传文件到Media文件夹
/// - 使用自定义文件存储管理文件
/// - 扩展文件管理功能
/// 
/// 设计原因：
/// - 展示Orchard Core文件存储系统的灵活性
/// - 演示如何集成和扩展文件存储服务
/// - 提供文件管理的最佳实践
/// - 支持多种存储后端
/// 
/// 设计模式：
/// - 依赖注入模式：通过构造函数注入文件存储服务
/// - 服务模式：使用专门的文件存储服务处理文件操作
/// - 路由模式：通过不同路由实现不同文件操作
/// - 通知模式：使用INotifier显示操作结果
/// </summary>
public sealed class FileManagementController : Controller
{
    // 将路径定义为常量，避免重复。
    private const string TestFileRelativePath = "TrainingDemo/TestFile1.txt";
    private const string UploadedFileFolderRelativePath = "TrainingDemo/Uploaded";

    private readonly IMediaFileStore _mediaFileStore;
    private readonly INotifier _notifier;
    private readonly IHtmlLocalizer H;
    private readonly ICustomFileStore _customFileStore;

    /// <summary>
    /// 构造函数 - 注入所需的服务依赖
    /// </summary>
    /// <param name="mediaFileStore">媒体文件存储服务，用于管理Media文件夹中的文件</param>
    /// <param name="notifier">通知服务，用于显示用户消息</param>
    /// <param name="htmlLocalizer">HTML本地化器，用于多语言支持</param>
    /// <param name="customFileStore">自定义文件存储服务，用于管理自定义文件夹</param>
    public FileManagementController(
        IMediaFileStore mediaFileStore,
        INotifier notifier,
        IHtmlLocalizer<FileManagementController> htmlLocalizer,
        ICustomFileStore customFileStore)
    {
        _mediaFileStore = mediaFileStore;
        _notifier = notifier;
        _customFileStore = customFileStore;
        H = htmlLocalizer;
    }

    /// <summary>
    /// 在Media文件夹中创建文件
    /// 
    /// 功能说明：
    /// - 在Media文件夹中创建一个文本文件
    /// - 写入指定内容
    /// - 返回文件大小和公开URL
    /// 
    /// 访问方式：
    /// - URL：/Lombiq.TrainingDemo/FileManagement/CreateFileInMediaFolder
    /// </summary>
    /// <returns>返回创建结果字符串</returns>
    // 该操作演示如何在Media文件夹中创建文件并从中读取。在/Lombiq.TrainingDemo/FileManagement/CreateFileInMediaFolder下查看。
    public async Task<string> CreateFileInMediaFolder()
    {
        // 如果您有特定文本要写入文件，需要初始化一个流。如果已经有流，直接使用即可！
        using (var stream = new MemoryStream(Encoding.UTF8.GetBytes("Hi there!")))
        {
            // 第三个参数可选 - 如果为true，则会覆盖已存在的文件。
            await _mediaFileStore.CreateFileFromStreamAsync(TestFileRelativePath, stream, overwrite: true);
        }

        // 使用此方法检查文件是否存在（如果文件不存在则为null）。类似于内置的FileInfo类，但不那么健壮。
        var fileInfo = await _mediaFileStore.GetFileInfoAsync(TestFileRelativePath);

        // IMediaFileStore有自己的特定方法，如将文件路径映射到公开URL。由于Media文件夹中的文件可从外部访问，这很方便。
        var publicUrl = _mediaFileStore.MapPathToPublicUrl(TestFileRelativePath);

        return $"Successfully created file! File size: {fileInfo.Length} bytes. Public URL: {publicUrl}";
    }

    // 创建文件后，您可以转到仪表板，在Assets下查看是否能看到它。也可以在App_Data/Sites/{TenantName}/Media/TrainingDemo文件夹中找到。

    /// <summary>
    /// 从Media文件夹读取文件
    /// 
    /// 功能说明：
    /// - 检查文件是否存在
    /// - 读取文件内容并返回
    /// 
    /// 访问方式：
    /// - URL：/Lombiq.TrainingDemo/FileManagement/ReadFileFromMediaFolder
    /// </summary>
    /// <returns>返回文件内容字符串</returns>
    // 该操作将读取您之前创建的文件。在/Lombiq.TrainingDemo/FileManagement/ReadFileFromMediaFolder下查看。
    public async Task<string> ReadFileFromMediaFolder()
    {
        // 这样可以检查给定文件是否存在。
        if (await _mediaFileStore.GetFileInfoAsync(TestFileRelativePath) == null)
        {
            return "Create the file first!";
        }

        // 如果要提取文件内容，使用StreamReader读取流。
        using var stream = await _mediaFileStore.GetFileStreamAsync(TestFileRelativePath);
        using var streamReader = new StreamReader(stream);
        var content = await streamReader.ReadToEndAsync(HttpContext.RequestAborted);

        return $"File content: {content}";
    }

    /// <summary>
    /// 文件上传页面
    /// 
    /// 功能说明：
    /// - 提供文件上传界面
    /// - 支持上传文件到Media文件夹
    /// 
    /// 访问方式：
    /// - URL：/Lombiq.TrainingDemo/FileManagement/UploadFileToMedia
    /// </summary>
    /// <returns>返回上传页面视图</returns>
    // 如果您想查看我们的演示上传组件的样子，请转到Views/FileManagement/UploadFileToMedia.cshtml。
    public ActionResult UploadFileToMedia() => View();

    /// <summary>
    /// 文件上传处理
    /// 
    /// 功能说明：
    /// - 处理文件上传请求
    /// - 将文件保存到Media文件夹
    /// - 显示上传成功通知
    /// 
    /// 访问方式：
    /// - POST /Lombiq.TrainingDemo/FileManagement/UploadFileToMedia
    /// </summary>
    /// <param name="file">上传的文件</param>
    /// <returns>重定向到上传页面</returns>
    [HttpPost, ActionName(nameof(UploadFileToMedia)), ValidateAntiForgeryToken]
    public async Task<ActionResult> UploadFileToMediaPost(IFormFile file)
    {
        if (!ModelState.IsValid || file == null) return BadRequest();

        // 可以使用Combine方法组合路径，基本等同于内置方法。
        var mediaFilePath = _mediaFileStore.Combine(UploadedFileFolderRelativePath, file.FileName);

        // 此时您已经有了流，直接用它创建文件。
        using (var stream = file.OpenReadStream())
        {
            await _mediaFileStore.CreateFileFromStreamAsync(mediaFilePath, stream);
        }

        await _notifier.InformationAsync(H["Successfully uploaded file!"]);

        return RedirectToAction(nameof(UploadFileToMedia));
    }

    // 下一站：Services/CustomFileStore.cs

    /// <summary>
    /// 在自定义文件夹中创建文件
    /// 
    /// 功能说明：
    /// - 使用自定义文件存储服务创建文件
    /// - 写入指定内容
    /// - 返回文件大小
    /// 
    /// 访问方式：
    /// - URL：/Lombiq.TrainingDemo/FileManagement/CreateFileInCustomFolder
    /// </summary>
    /// <returns>返回创建结果字符串</returns>
    // 在/Lombiq.TrainingDemo/FileManagement/CreateFileInCustomFolder下查看。
    public async Task<string> CreateFileInCustomFolder()
    {
        // 现在过程与IMediaFileStore相同，但这次是我们的ICustomFileStore。
        // 文件将被创建在我们在Startup.cs中定义的CustomFiles文件夹中。
        using (var stream = new MemoryStream(Encoding.UTF8.GetBytes("Hi there in the custom file storage!")))
        {
            await _customFileStore.CreateFileFromStreamAsync(TestFileRelativePath, stream, overwrite: true);
        }

        var fileInfo = await _customFileStore.GetFileInfoAsync(TestFileRelativePath);

        return $"Successfully created file in the custom file storage! File size: {fileInfo.Length} bytes.";
    }
}

// 训练部分结束：文件管理

// 下一站：Services/DemoBackgroundTask.cs
